#include <map>

// usrtos header
#include <logs.hpp>
#include <loglevel.hpp>
#include <findblock.hpp>
#include <layout.hpp>
#include <exceptions.hpp>
#include <raw.hpp>

namespace usrtos {
class CTX {
private:
    map<uuid,CPBlock*>     m_blocks;
    map<string,uuid>      m_memName;
    CPBlock::MemoryMap     m_memmap;
    LogLevel				_SYSLOG;
    LogLevel				 _DEBUG;
    LogLevel				  _INFO;
    LogLevel				  _WARN;
    LogLevel				 _ERROR;
    LogLevel				 _FATAL;
    Layout::UsrtMem       *m_memory;
    logs                    *m_logs;

public:
    template<typename T>
    T* bindBlock(string name) {
        auto it = m_memName.find(name);
        T *r;
        if(it != m_memName.end() ) {
            r = new T(*(m_blocks[it->second]));
        }
        else {
            stringstream err;
            err << "in workers bindBlock: " << name << " failure" << endl;
            throw(usrtos_exception(err));
            r = nullptr;
        }
        return r;
    };

    template<typename T>
    T* bindBlockByKey(uuid id) {
        auto it = m_blocks.find(id);
        T *r;
        if(it != m_blocks.end() ) {
            r = new T(*(it->second));
        }
        else {
            stringstream err;
            err << "in workers bindBlockByKey: " << id << " failure" << endl;
            throw(usrtos_exception(err));
            r = nullptr;
        }
        return r;
    };

    CTX( const char* dir ) {
        FindBlock fb(dir);
        auto heads = fb.list();
        auto blocks = fb.attach(heads);
        for(auto it=blocks.begin();it != blocks.end(); ++it) {
            auto key = it->second->getKey();
            m_blocks[key] = it->second;
            m_memmap[key] = it->second->getRegion();
            m_memName[it->second->getName()] = key;
        }
        
        m_memory = bindBlock<Layout::UsrtMem>("memory");
        m_logs = new logs(blocks);
        
        SYSLOG  = LogLevel(*m_logs,0);
        DEBUG	= LogLevel(*m_logs,1);
        INFO	= LogLevel(*m_logs,2);
        WARN	= LogLevel(*m_logs,3);
        ERROR	= LogLevel(*m_logs,4);
        FATAL	= LogLevel(*m_logs,5);
        _SYSLOG = LogLevel(*m_logs,0);
        _DEBUG	= LogLevel(*m_logs,1);
        _INFO	= LogLevel(*m_logs,2);
        _WARN	= LogLevel(*m_logs,3);
        _ERROR	= LogLevel(*m_logs,4);
        _FATAL	= LogLevel(*m_logs,5);

    };
    uuid getMemKey(string name) {
        auto it = m_memName.find(name);
        if(it == m_memName.end()) {
            stringstream err;
            err << "in workers getMemKey: miss key: " << name << std::endl;
            throw(usrtos_exception(err));
        }
        return it->second;
    };
    template <typename T>
    T* G2L(CPBlock::GP& gp) {
        if(m_blocks.count(gp.id) > 0)
            return m_blocks[gp.id]->GP2LP<T>(gp);
        else {
            stringstream err;
            err << "in workers G2L: error gp " << gp.id << std::endl;
            throw(usrtos_exception(err,10));
            return nullptr;
        }
    };

    template <typename T>
    bool L2G(CPBlock::GP& gp, T* p) {
        for(auto it = m_blocks.begin();it != m_blocks.end();++it) {
            if(it->second->validAddress(p)) {
                return it->second->LP2GP(gp,p);
            }
        }
        stringstream err;
        err << "in workers L2G: address invaliable: " << p << std::endl;
        throw(usrtos_exception(err));
        return false;
    };
    int log_len(int l) {
        std::stringstream ln;
		ln << "log" << l;
		return (*m_logs)[ln.str()]->len();
    };
    usrtlog::logitem *getLog(int l) {
        std::stringstream ln;
		ln << "log" << l;
		return (*m_logs)[ln.str()]->getLog();
    };
};
};